﻿Public Class Futoshiki
    Inherits Puzzle

    Public Overloads Sub DrawCell(ByVal Cell As Cell)
        If Selection.Contains(Cell) Then DrawHighlight(Cell)
        If Cell.g <> " " Then
            If Cell.Type = 1 Then
                DrawGiven(Cell, Cell.g, False)
            Else
                Dim Pen = New Pen(Color.White, 3)
                Pen.SetLineCap(Drawing2D.LineCap.Round, Drawing2D.LineCap.Round, Drawing2D.DashCap.Round)
                Dim R = Cell.Rect
                R.Inflate(-R.Width \ 3, -R.Width \ 3)
                Gfx.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
                Gfx.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
                If Cell.x Mod 2 = 0 Then
                    R.Inflate(-R.Width \ 6, 0)
                    If Cell.g = ">" Then
                        Gfx.DrawLine(Pen, R.Left, R.Top, R.Right, R.Top + R.Height \ 2)
                        Gfx.DrawLine(Pen, R.Left, R.Bottom, R.Right, R.Top + R.Height \ 2)
                    ElseIf Cell.g = "<" Then
                        Gfx.DrawLine(Pen, R.Right, R.Top, R.Left, R.Top + R.Height \ 2)
                        Gfx.DrawLine(Pen, R.Right, R.Bottom, R.Left, R.Top + R.Height \ 2)
                    End If
                ElseIf Cell.y Mod 2 = 0 Then
                    R.Inflate(0, -R.Width \ 6)
                    If Cell.g = ">" Then
                        Gfx.DrawLine(Pen, R.Left, R.Top, R.Left + R.Width \ 2, R.Bottom)
                        Gfx.DrawLine(Pen, R.Right, R.Top, R.Left + R.Width \ 2, R.Bottom)
                    ElseIf Cell.g = "<" Then
                        Gfx.DrawLine(Pen, R.Left, R.Bottom, R.Left + R.Width \ 2, R.Top)
                        Gfx.DrawLine(Pen, R.Right, R.Bottom, R.Left + R.Width \ 2, R.Top)
                    End If
                End If
                Gfx.SmoothingMode = Drawing2D.SmoothingMode.Default
                Gfx.PixelOffsetMode = Drawing2D.PixelOffsetMode.Default
            End If
        ElseIf Cell.f = "" Then
            DrawPencilmarks(Cell, Cell.CandidateStr)
        Else
            DrawFound(Cell, Cell.f)
        End If

    End Sub

    Public Overrides Sub Draw()
        If Cells Is Nothing Then Exit Sub
        For Each Cell As Cell In Cells
            If Cell.Type = 1 Then
                FillRect(Cell.x, Cell.y, 1, 1, Color.FromArgb(224, Color.White))
                DrawRect(Cell.x, Cell.y, 1, 1, 1)
            End If
            DrawCell(Cell)
        Next
    End Sub

    Public Overrides Sub NewPuzzle()
        Cursor = New Point(1, 1)
        Cells = New Cells
        Groups = New Groups
        For i As Integer = 0 To Size * Size - 1
            Dim Cell As New Cell(Me, (i Mod Size) + 1, (i \ Size) + 1)
            Cell.Scale = 1.3
            Cells.Add(Cell)
        Next
        For Each Cell As Cell In From c In Cells Where c.x Mod 2 = 0 Or c.y Mod 2 = 0
            Cell.Type = 2
            Cell.Scale = 0.7
        Next
        For Each Cell As Cell In From c In Cells Where c.x Mod 2 = 0 And c.y Mod 2 = 0
            Cell.Type = 3
            Cell.Scale = 0
        Next
        For t = 1 To 2
            For i As Integer = 1 To (Size + 1) \ 2 - 1
                'Groups.Add(New Group(t, i))
            Next
        Next
        For Each Item In Groups
            Dim Group = Item
            'Group.Cells = (From c In Cells Where c.Type = 1 And c.Index(Group.Type) = Group.Index).ToList
            'Group.Followers = (From g In Groups Where g > Group).ToList
        Next
        For Each Item In Cells
            Dim Cell = Item
            'Cell.LDirections.Add(Cell.Groups(1))
            'Cell.LDirections.Add(Cell.Groups(2))
            'Cell.Grp.Add((From g In Groups Where g.Type = 1 And g.Index = Cell.x).First)
            'Cell.Grp.Add((From g In Groups Where g.Type = 2 And g.Index = Cell.y).First)
        Next
        Invalidate()
    End Sub

    Public Overrides Function OnKeyPress(ByVal Key As String) As Boolean
        Dim Cell As Cell = (From c As Cell In Cells Where c.x = Cursor.X And c.y = Cursor.Y Select c).First
        Select Case Key
            Case "1" To "9"
                If Cell.Type = 1 Then
                    Cell.g = Key
                    Cell.Invalidate()
                    If AutosolveMode Then Solve()
                End If
            Case "delete"
                Cell.g = " "
                Cell.Invalidate()
                If AutosolveMode Then Solve()
            Case "<", ">"
                If Cell.Type = 2 Then
                    Cell.g = Key
                    Cell.Invalidate()
                    If AutosolveMode Then Solve()
                End If
        End Select
        Return MyBase.OnKeyPress(Key)
    End Function

    Public Overrides Sub Load(ByVal Data As String)
        Size = Math.Sqrt(Len(Data))
        NewPuzzle()
        For i As Integer = 1 To Size * Size
            Dim Cell As Cell = Cells(i - 1)
            Cell.g = Mid(Data, i, 1)
        Next
        Invalidate()
    End Sub

    Public Overrides Function Save() As String
        Dim Text As String = ""
        For i As Integer = 1 To Size * Size
            Dim Cell As Cell = Cells(i - 1)
            Text &= Cell.g
        Next
        Return Text
    End Function

    Dim Comparisons As List(Of Comparison)

    Public Overrides Sub Solve()
        ClearLog()
        For Each Cell In Cells
            Cell.Reset(Mid("123456789", 1, (Size + 1) / 2))
        Next
        For Each Group In Groups
            Group.Reset()
        Next
        Comparisons = New List(Of Comparison)
        For Each Cell In From c In Cells Where c.Type = 2 And c.g <> " "
            Dim Cell1 = If((Cell.x Mod 2) = 0, Find(Cell.x - 1, Cell.y), Find(Cell.x, Cell.y - 1))
            Dim Cell2 = If((Cell.x Mod 2) = 0, Find(Cell.x + 1, Cell.y), Find(Cell.x, Cell.y + 1))
            If Cell.g = ">" Then Comparisons.Add(New Comparison(Cell1, Cell2)) Else Comparisons.Add(New Comparison(Cell2, Cell1))
        Next
        ProcessGiven()
        Dim Level As Integer = 1
        Do
            Dim Progress As Boolean = False
            Select Case Level
                Case 1 : Progress = ProcessComparisons()
                Case 2 : Progress = NakedCandidates()
                Case 3 : Progress = SingleCandidates()
                Case 4 : Progress = NakedPairs()
                Case 5 : Progress = HiddenPairs()
                Case 6 : Progress = NakedTriples()
                Case 7 : Progress = HiddenTriplets()
                Case 8 : Progress = XWing()
                Case 9 : Progress = SwordFish()
                Case 10 : Progress = JellyFish()
                Case Else : If Not Progress Then Exit Do
            End Select
            Level = IIf(Progress, 1, Level + 1)
            FlushLog()
            If (From c As Cell In Cells Where c.f = "" Select c).Count = 0 Then Exit Do
        Loop
        Invalidate()
    End Sub

    Sub ProcessGiven()
        For Each Result In From Cell In Cells Where Cell.g <> " " Select Cell
            Result.SetValue(Result.g)
        Next
    End Sub

    Function ProcessComparisons() As Boolean
        Dim Progress As Boolean
        For Each Result In (From p In Comparisons From c In p.Cell2.Candidates Where c >= p.Cell1.Max).ToList
            Progress = True
            Result.p.Cell2.Eliminate(Result.c)
        Next
        For Each Result In (From p In Comparisons From c In p.Cell1.Candidates Where c <= p.Cell2.Min).ToList
            Progress = True
            Result.p.Cell1.Eliminate(Result.c)
        Next
        Return Progress
    End Function

    Public Sub New()
        Size = 9
        AvailableSizes = (From n In Enumerable.Range(5, 5) Select n * 2 - 1).ToList
    End Sub

    Class Comparison
        Public Cell1, Cell2 As Cell

        Sub New(ByVal Cell1 As Cell, ByVal Cell2 As Cell)
            Me.Cell1 = Cell1
            Me.Cell2 = Cell2
        End Sub

    End Class

End Class

